// ValidationAspects
// Written by Mike Saunders
// Released under Microsoft Public License (Ms-PL)
using System;
using System.Collections.Generic;
using System.Reflection;
using NUnit.Framework;
using ValidationAspects;
using ValidationAspects.Configuration;
using ValidationAspects.Sdk;

namespace TestValidationAspects.Configuration
{
	[TestFixture]
	public class TestMethodValidation
	{
		[SetUp]
		public void SetUp()
		{
			TestHelpers.ResetFramework();
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_Registry_throws_for_null_param()
		{
			new MethodValidation().Register(null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_Register_throws_if_method_is_null()
		{
			new MethodValidation().Register(GetType());
		}

		[Test]
		public void Test_Register_throws_if_method_is_unknown()
		{
			Test.Throws<ValidationConfigurationException>(() => new MethodValidation {Method = "unknown"}.Register(typeof(Dummy)),
			                                              string.Format("Method [name:{0}] [type:{1}] is unknown.", "unknown", typeof(Dummy)));
		}

		[Test]
		public void Test_Register_with_no_supplied_parameters_is_harmless()
		{
			new MethodValidation {Method = "Void Method(System.String)"}.Register(typeof(Dummy));
		}

		[Test]
		public void Test_Configure_correctly_calls_onto_supplier_IPropertyValidation_instances()
		{
			var mock = new MockParameterValidation();
			Assert.IsNull(mock.MethodInfo);

			new MethodValidation {Method = "Void Method(System.String)", Parameters = new List<IParameterValidation> {mock}}.Register(typeof(Dummy));

			Assert.AreSame(typeof(Dummy).GetMethod("Method"), mock.MethodInfo);
		}

		[Test]
		public void Test_Register_with_Apply_Add_adds_factories()
		{
			MockValidatorFactory<Dummy> factoryA = new MockValidatorFactory<Dummy>();
			Assert.IsNull(factoryA.Value);

			MethodInfo methodInfo = typeof(Dummy).GetMethod("Method");
			methodInfo.ReplaceValidation(new[] {factoryA});

			MockValidatorFactory<Dummy> factoryB = new MockValidatorFactory<Dummy>();
			Assert.IsNull(factoryB.Value);

			MethodValidation methodValidation = new MethodValidation {Apply = ValidationApplication.Add, Method = "Void Method(System.String)", Validators = new List<IValidatorFactory> {factoryB}};

			methodValidation.Register(typeof(Dummy));

			Dummy value = new Dummy();
			IValidationContext context = new MockValidationContext();
			methodInfo.ResolveValidator().Invoke(value, context);

			factoryA.TestState(value, context);
			factoryB.TestState(value, context);
		}

		[Test]
		public void Test_Register_with_Apply_Replace_replaces_factories()
		{
			MockValidatorFactory<Dummy> factoryA = new MockValidatorFactory<Dummy>();
			Assert.IsNull(factoryA.Value);

			MethodInfo methodInfo = typeof(Dummy).GetMethod("Method");
			methodInfo.ReplaceValidation(new[] {factoryA});

			MockValidatorFactory<Dummy> factoryB = new MockValidatorFactory<Dummy>();
			Assert.IsNull(factoryB.Value);

			MethodValidation methodValidation = new MethodValidation {Apply = ValidationApplication.Replace, Method = "Void Method(System.String)", Validators = new List<IValidatorFactory> {factoryB}};

			methodValidation.Register(typeof(Dummy));

			Dummy value = new Dummy();
			IValidationContext context = new MockValidationContext();
			methodInfo.ResolveValidator().Invoke(value, context);

			factoryA.TestState(null, null);
			factoryB.TestState(value, context);
		}

		[Test]
		public void Test_Register_with_no_supplied_validators_is_harmless()
		{
			new MethodValidation {Method = "Void Method(System.String)"}.Register(typeof(Dummy));
			Assert.IsNull(typeof(Dummy).GetMethod("Method").ResolveValidator());
		}

		#region Mocks
		public class Dummy
		{
			public void Method(string s) {}
		}

		public class MockParameterValidation : IParameterValidation
		{
			public MethodInfo MethodInfo { get; set; }

			#region Implementation of IParameterValidation
			public void Register(MethodInfo methodInfo)
			{
				MethodInfo = methodInfo;
			}
			#endregion
		}
		#endregion
	}
}